package cn.bbstone.pisces2.config;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.net.URI;
import java.net.URL;
import java.util.Enumeration;
import java.util.Properties;

import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.sync.ReadWriteSynchronizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.bbstone.pisces2.comm.Const;

public class ConfigImpl implements Config {
    private Logger log = LoggerFactory.getLogger(ConfigImpl.class);

    private String SERVER_CONFIG_FILE = "config.server.properties";
    private String CLIENT_CONFIG_FILE = "config.client.properties";

    //	private String defaultConfigFile = "config.default.properties";
    private PropertiesConfiguration configs = new PropertiesConfiguration();
    private ConfigModel configModel = new ConfigModel();
    private Properties props = new Properties();

    public ConfigImpl() {
    }

    private File getConfigFile(String configFile, ClassLoader classLoader) {
        URI uri = null;
        try {
            if (classLoader != null) { // run as eclipse bundle
                Enumeration<URL> urls = classLoader.getResources(configFile);
                if (urls.hasMoreElements()) {
                    URL url = urls.nextElement();
                    props.load(url.openStream());
                }
            } else {
                uri = Thread.currentThread().getContextClassLoader().getResource(configFile).toURI();
                return new File(uri);
            }
        } catch (Exception e) {
            log.error("load config.properties error", e);
        }
        return null;
    }

    @Override
    public ConfigModel load(String serverOrClient, ClassLoader classLoader) {
        try {
            String configFile = Const.SERVER.equals(serverOrClient) ? SERVER_CONFIG_FILE : CLIENT_CONFIG_FILE;
            if (classLoader != null) {
                props.clear();
                getConfigFile(configFile, classLoader);
                // common
                configModel.setFilter(String.valueOf(props.getProperty(ConfigModel.FLI_FILTER_KEY)));
                // server
                configModel.setServerHost(String.valueOf(props.getProperty(ConfigModel.SERVER_HOST_KEY)));
                configModel.setServerPort(Integer.valueOf(props.getProperty(ConfigModel.SERVER_PORT_KEY)));
                if (Const.SERVER.equals(serverOrClient)) {
                    configModel.setServerRootMac(String.valueOf(props.getProperty(ConfigModel.SERVER_DIR_MAC_KEY)));
                    configModel.setServerRootNix(String.valueOf(props.getProperty(ConfigModel.SERVER_DIR_NIX_KEY)));
                    configModel.setServerRootWin(String.valueOf(props.getProperty(ConfigModel.SERVER_DIR_WIN_KEY)));
                }
                // client
                if (Const.CLIENT.equals(serverOrClient)) {
                    configModel.setClientRootMac(String.valueOf(props.getProperty(ConfigModel.CLIENT_DIR_MAC_KEY)));
                    configModel.setClientRootNix(String.valueOf(props.getProperty(ConfigModel.CLIENT_DIR_NIX_KEY)));
                    configModel.setClientRootWin(String.valueOf(props.getProperty(ConfigModel.CLIENT_DIR_WIN_KEY)));
                    
                    configModel.setPostfix(String.valueOf(props.getProperty(ConfigModel.FILE_TEMP_POSTFIX_KEY)));
                    configModel.setOverwrite(Boolean.valueOf(props.getProperty(ConfigModel.CLIENT_FILE_OVERWRITE)));
                    configModel.setConnNum(Integer.valueOf(props.getProperty(ConfigModel.CLIENT_CONN_NUM_KEY)));
                    configModel.setRecvBuffer(String.valueOf(props.getProperty(ConfigModel.CLIENT_RECV_BUFFER_KEY)));
                }
            } else {
//            URI uri = ConfigImpl.class.getClassLoader().getResource(configFile).toURI();
                configs.clear();
                configs.read(new FileReader(getConfigFile(configFile, classLoader))); // new File(uri)
                configs.setSynchronizer(new ReadWriteSynchronizer());
//            props.load(new FileReader(getConfigFile(configFile)));
                // common
                configModel.setFilter(String.valueOf(configs.getProperty(ConfigModel.FLI_FILTER_KEY)));
                // server
                configModel.setServerHost(String.valueOf(configs.getProperty(ConfigModel.SERVER_HOST_KEY)));
                configModel.setServerPort(configs.getInt(ConfigModel.SERVER_PORT_KEY));
                if (Const.SERVER.equals(serverOrClient)) {
                    configModel.setServerRootNix(String.valueOf(configs.getProperty(ConfigModel.SERVER_DIR_NIX_KEY)));
                    configModel.setServerRootWin(String.valueOf(configs.getProperty(ConfigModel.SERVER_DIR_WIN_KEY)));
                }
                // client
                if (Const.CLIENT.equals(serverOrClient)) {
                    configModel.setClientRootNix(String.valueOf(configs.getProperty(ConfigModel.CLIENT_DIR_NIX_KEY)));
                    configModel.setClientRootWin(String.valueOf(configs.getProperty(ConfigModel.CLIENT_DIR_WIN_KEY)));
                    
                    configModel.setPostfix(String.valueOf(configs.getProperty(ConfigModel.FILE_TEMP_POSTFIX_KEY)));
                    configModel.setOverwrite(configs.getBoolean(ConfigModel.CLIENT_FILE_OVERWRITE));
                    configModel.setConnNum(configs.getInt(ConfigModel.CLIENT_CONN_NUM_KEY));
                    configModel.setRecvBuffer(String.valueOf(configs.getProperty(ConfigModel.CLIENT_RECV_BUFFER_KEY)));
                }
            }

        } catch (Exception ex) {
            log.error("parse config.properties error XXX", ex);
        }
        return configModel;
    }


    @Override
    public ConfigModel getModel() {
        return configModel;
    }

    @Override
    public void store(ConfigModel config) {

    }


    public PropertiesConfiguration configs() {
        return configs;
    }


    public final String ENABLED = "Enabled";
    public final String DISABLED = "Disabled";

    public final String TRUE = "true";
    public final String FALSE = "false";

//    private final String EMPTY_STR = "";

    public String recvBufferSize() {
        return configs.getString(ConfigModel.CLIENT_RECV_BUFFER_KEY, "32M");
    }

    /**
     * only support M/m or K/k, e.g. 32M/32m, 32K/32k
     *
     * @return - value with unit B(Byte), because buffer store bytes.
     */
    public int recvBufferSizeInt() {
        try {
            String configBufferSize = recvBufferSize();
            String bufferSizeNum = configBufferSize.substring(0, configBufferSize.length() - 1);
            if (configBufferSize.endsWith("M") || configBufferSize.endsWith("m")) {
                return Integer.valueOf(bufferSizeNum) * 1024 * 1024;
            } else if (configBufferSize.endsWith("K") || configBufferSize.endsWith("k")) {
                return Integer.valueOf(bufferSizeNum) * 1024;
            }
        } catch (Exception e) {
            log.warn(String.format("get client receive buffer size error, will used default value(%d MB).", Const.DEFAULT_CLIENT_RECV_BUFFER_SIZE / (1024 * 1024)), e);
        }
        return Const.DEFAULT_CLIENT_RECV_BUFFER_SIZE;
    }

    public String tempFilePostfix() {
        return configs.getString(ConfigModel.FILE_TEMP_POSTFIX_KEY);
    }

    public boolean isClientFileOverwrite() {
        return configs.getBoolean(ConfigModel.CLIENT_FILE_OVERWRITE);
    }

//    private final String CLASSPATH_PREFIX = "classpath:";
//    private File readClasspathFile(String path) {
//        File file = null;
//        if (StringUtils.isNotBlank(path) && path.startsWith(CLASSPATH_PREFIX)) {
//            String classpath = path.substring(CLASSPATH_PREFIX.length());
//            classpath = BFileUtil.convertToLocalePath(classpath);
//            log.debug("path: {}, classpath: {}", path, classpath);
//            URI uri = null;
//            try {
//                uri = Thread.currentThread().getContextClassLoader().getResource(classpath).toURI();
//                log.debug("uri: {}", uri.toString());
//            } catch (URISyntaxException e) {
//                log.error("read classpath file error.", e);
//            }
//            file = new File(uri);
//        }
//        return file;
//    }


    /**
     * only update property in the cache, not save to file
     *
     * @param key
     * @param value
     */
    public void setProperty(String key, Object value) {
        configs.setProperty(key, value);
    }

    /**
     * update properties cache and save the update to disk(file)
     *
     * @param key
     * @param value
     */
    public void setPropertyAndSave(String key, Object value) {
        setProperty(key, value);
        saveUpdate();
    }

    /**
     * batch save the update to file(disk)
     */
    public void saveUpdate() {
        try {
            configs.getLayout().save(configs, new FileWriter(getConfigFile(SERVER_CONFIG_FILE, null)));
        } catch (Exception e) {
            log.error("save config file update error", e);
        }
    }


//	public void main(String[] args) {
//		System.out.println(test());
//		setProperty("test.a", "YY12Y");
//		setProperty("test.b", 113222);
//		saveUpdate();
//		System.out.println(test());
//	}

}
